package com.example.hzzrpcclient.util;

import com.sun.istack.internal.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;

/**
 * @author ：hzz
 * @description：TODO
 * @date ：2022/1/2 23:06
 */
public class GuardedObject<T> {
    private static final Logger log = LoggerFactory.getLogger(GuardedObject.class);
    private static final Map<Object, GuardedObject> gos = new ConcurrentHashMap();
    private static final int awaitTimeOut = 50;
    private final Lock lock = new ReentrantLock();
    private final Condition done;
    private T obj;
    private Object key;

    private GuardedObject(Object key) {
        this.done = this.lock.newCondition();
        this.key = key;
    }

    public static <T> GuardedObject<T> create(@NotNull Object key) {
        GuardedObject<T> go = new GuardedObject(key);
        gos.put(key, go);
        return go;
    }

    public static boolean containsEvent(Object key) {
        return gos.containsKey(key);
    }

    public static <T> boolean fireEvent(Object key, T obj) {
        GuardedObject go = (GuardedObject)gos.remove(key);
        if (go != null) {
            go.onChange(obj);
        }

        return go != null;
    }

    public T getAndThrow(Predicate<T> p, int timeOut) throws InterruptedException {
        Object var5;
        try {
            long maxTime = System.currentTimeMillis() + (long)timeOut;
            this.lock.lock();

            for(; this.obj == null || !p.test(this.obj); this.done.await(10L, TimeUnit.MILLISECONDS)) {
                if (System.currentTimeMillis() > maxTime) {
                    Thread.currentThread().interrupt();
                }
            }

            var5 = this.obj;
        } finally {
            gos.remove(this.key);
            this.lock.unlock();
        }

        return (T) var5;
    }

    public T getAndThrow(Predicate<T> p, int duration, TimeUnit timeUnit) throws InterruptedException {
        Object var6;
        try {
            long maxTime = System.currentTimeMillis() + timeUnit.toMillis((long)duration);
            this.lock.lock();

            for(; this.obj == null || !p.test(this.obj); this.done.await(50L, TimeUnit.MILLISECONDS)) {
                if (System.currentTimeMillis() > maxTime) {
                    Thread.currentThread().interrupt();
                }
            }

            var6 = this.obj;
        } finally {
            gos.remove(this.key);
            this.lock.unlock();
        }

        return (T) var6;
    }

    private void onChange(T obj) {
        this.lock.lock();

        try {
            this.obj = obj;
            this.done.signalAll();
        } finally {
            this.lock.unlock();
        }

    }
}
